package com.wonders.common.ws.server;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.wonders.common.constant.CommonConstant;
import com.wonders.common.exception.XtptBusinessException;
import com.wonders.common.exception.XtptTradeException;
import com.wonders.common.parse.jaxb.JaxbService;
import com.wonders.common.ws.constant.BusiCode;
import com.wonders.common.ws.constant.TradeCode;
import com.wonders.common.ws.constant.XtptConstant;
import com.wonders.common.ws.entity.vo.XtptMsg;
import com.wonders.common.ws.mapping.bean.global.Head;
import com.wonders.common.ws.server.excutor.BaseXtptWsExcutor;
import com.wonders.common.ws.util.XtptUtil;
import com.wonders.framework.exception.FrameworkException;
import com.wonders.framework.util.StreamUtil;
import com.wonders.framework.util.web.ApplicationUtil;
import com.wonders.module.ws.excutor.XtptWsExcutorFactory;

/**
 * 协同平台webservice服务
 * 
 * @author WuBin
 * 
 */
public class XtptWsServer {

	private Logger logger = LoggerFactory.getLogger(XtptWsServer.class);

	/**
	 * 接口入口
	 * 
	 * @param request
	 *            请求数据
	 * @return 响应数据
	 */
	public byte[] dispatch(byte[] request) {
		Head reqHead = null;
		XtptMsg xtptRequest = null;
		XtptMsg xtptResponse = null;
		try {
			if (request == null) {
				throw new XtptTradeException(TradeCode.REQUEST_PARAMS_ERROR,
						"请求参数不能为空");
			}
			try {
				xtptRequest = resovleRequest(request);
			} catch (FrameworkException e) {
				throw new XtptTradeException(TradeCode.REQUEST_PARAMS_ERROR,
						"请求参数无法解析", e.getMessage(), e);
			}
			if (StringUtils.isEmpty(xtptRequest.getHeadXml())) {
				throw new XtptTradeException(TradeCode.REQUEST_HEAD_ERROR,
						"请求报文头有误");
			}
			try {
				reqHead = (Head) ApplicationUtil.getBean(JaxbService.class)
						.fromXML(XtptConstant.GLOBAL_HEAD,
								CommonConstant.Interaction.REQ,
								xtptRequest.getHeadXml());
			} catch (FrameworkException e) {
				throw new XtptTradeException(TradeCode.REQUEST_HEAD_ERROR,
						"请求报文头有误", e.getMessage(), e);
			}
			xtptRequest.setHead(reqHead);
			BaseXtptWsExcutor baseWsExcutor = ApplicationUtil.getBean(
					XtptWsExcutorFactory.class).getWsExcutor(
					reqHead.getBusCode());
			if (baseWsExcutor == null) {
				throw new XtptTradeException(TradeCode.INTERFACE_UNDEFINED,
						"调用接口未定义");
			}
			String key = StringUtils.join(reqHead.getSysCode(), "_",
					reqHead.getBusCode());
			Object reqdata = null;
			if (StringUtils.isNotBlank(xtptRequest.getBodyXml())) {
				try {
					reqdata = ApplicationUtil.getBean(JaxbService.class)
							.fromXML(key, CommonConstant.Interaction.REQ,
									xtptRequest.getBodyXml());
				} catch (FrameworkException e) {
					throw new XtptTradeException(TradeCode.REQUEST_BODY_ERROR,
							"请求报文体有误", e.getMessage(), e);
				}
			}
			xtptRequest.setBody(reqdata);
			try {
				xtptResponse = baseWsExcutor.excute(xtptRequest);
			} catch (Exception e) {
				if (e instanceof XtptTradeException
						|| e instanceof XtptBusinessException) {
					throw e;
				}
				throw new XtptBusinessException(BusiCode.FAILED, "业务处理失败",
						"业务处理失败", e);
			}
			Head resHead = XtptUtil.generateResponseHead(reqHead, xtptResponse);
			try {
				xtptResponse.setHeadXml(ApplicationUtil.getBean(
						JaxbService.class).toXml(XtptConstant.GLOBAL_HEAD,
						CommonConstant.Interaction.RES, resHead));
				if (xtptResponse.getBody() != null) {
					xtptResponse.setBodyXml(ApplicationUtil.getBean(
							JaxbService.class).toXml(key,
							CommonConstant.Interaction.RES,
							xtptResponse.getBody()));
				}
			} catch (FrameworkException e) {
				throw new XtptTradeException(TradeCode.RESPONSE_PARAMS_ERROR,
						"响应报文有误", e.getMessage(), e);
			}
			byte[] response = null;
			try {
				response = packResponse(xtptResponse);
			} catch (FrameworkException e) {
				throw new XtptTradeException(TradeCode.RESPONSE_PARAMS_ERROR,
						"响应参数无法解析", e.getMessage(), e);
			}
			return response;
		} catch (Exception e) {
			if (e.getMessage() != null) {
				logger.error(e.getMessage(), e);
			} else {
				if (e instanceof XtptTradeException
						|| e instanceof XtptBusinessException) {

				} else {
					logger.error("处理接口请求失败", e);
				}
			}
			xtptResponse = new XtptMsg();
			if (e instanceof XtptTradeException) {
				xtptResponse.setTradeCode(((XtptTradeException) e)
						.getTradeCode());
				xtptResponse.setInfo(((XtptTradeException) e).getInfo());
			} else if (e instanceof XtptBusinessException) {
				xtptResponse.setTradeCode(TradeCode.SUCCESS);
				xtptResponse.setBusiCode(((XtptBusinessException) e)
						.getBusiCode());
				xtptResponse.setInfo(((XtptBusinessException) e).getInfo());
			} else {
				xtptResponse.setTradeCode(TradeCode.RESOLVE_REQUEST_ERROR);
				xtptResponse.setInfo("处理接口请求失败");
			}
			try {
				Head resHead = XtptUtil.generateResponseHead(reqHead,
						xtptResponse);
				xtptResponse.setHeadXml(ApplicationUtil.getBean(
						JaxbService.class).toXml(XtptConstant.GLOBAL_HEAD,
						CommonConstant.Interaction.RES, resHead));
				return packResponse(xtptResponse);
			} catch (Exception ex) {
				logger.error("返回通用异常报文失败", ex);
				return null;
			}
		} finally {
			logger.info("请求报文头:{}, 请求报文体:{}, 响应报文头:{}, 响应报文体:{}",
					xtptRequest != null ? xtptRequest.getHeadXml() : "",
					xtptRequest != null ? xtptRequest.getBodyXml() : "",
					xtptResponse != null ? xtptResponse.getHeadXml() : "",
					xtptResponse != null ? xtptResponse.getBodyXml() : "");
		}
	}

	/**
	 * 解析请求数据
	 * 
	 * @param request
	 *            请求数据
	 * @return 请求数据
	 */
	private XtptMsg resovleRequest(byte[] request) {
		DataInputStream dataInputStream = null;
		try {
			ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
					request);
			dataInputStream = new DataInputStream(byteArrayInputStream);
			short headlen = dataInputStream.readShort();
			int bodylen = dataInputStream.readInt();
			dataInputStream.readByte();
			byte[] headbytes = StreamUtil.read(dataInputStream, headlen);
			byte[] bodybytes = StreamUtil.read(dataInputStream, bodylen);
			XtptMsg xtptRequest = new XtptMsg();
			xtptRequest
					.setHeadXml(new String(headbytes, XtptConstant.ENCODING));
			xtptRequest
					.setBodyXml(new String(bodybytes, XtptConstant.ENCODING));
			return xtptRequest;
		} catch (Exception e) {
			throw new FrameworkException("解析请求数据失败", e);
		} finally {
			if (dataInputStream != null) {
				try {
					dataInputStream.close();
				} catch (IOException e) {
					logger.error("关闭流失败", e);
				}
			}
		}
	}

	/**
	 * 组装响应数据
	 * 
	 * @param xtptResponse
	 *            响应数据
	 * @return 请求数据
	 */
	private byte[] packResponse(XtptMsg xtptResponse) {
		ByteArrayOutputStream byteArrayOutputStream = null;
		DataOutputStream dataOutputStream = null;
		try {
			byteArrayOutputStream = new ByteArrayOutputStream();
			dataOutputStream = new DataOutputStream(byteArrayOutputStream);
			byte[] head = xtptResponse.getHeadXml().getBytes(
					XtptConstant.ENCODING);
			dataOutputStream.writeShort(head.length);
			byte[] body = null;
			if (StringUtils.isNotBlank(xtptResponse.getBodyXml())) {
				body = xtptResponse.getBodyXml().getBytes(XtptConstant.ENCODING);
				dataOutputStream.writeInt(body.length);
			} else {
				dataOutputStream.writeInt(0);
			}
			dataOutputStream.writeByte(0x00);
			dataOutputStream.write(head);
			if (StringUtils.isNotBlank(xtptResponse.getBodyXml())) {
				dataOutputStream.write(body);
			}
			dataOutputStream.flush();
		} catch (Exception e) {
			throw new FrameworkException("组装响应数据失败", e);
		} finally {
			if (dataOutputStream != null) {
				try {
					dataOutputStream.close();
				} catch (IOException e) {
					logger.error("关闭流失败", e);
				}
			}
		}
		return byteArrayOutputStream.toByteArray();
	}

}
